﻿using System.Collections;
using System.Collections.Generic;

namespace FunctionalProgramming.LazyCollections
{
   /// <summary>
   /// Creates elements of provided lazy collection by repeatedly invoking evaluation function.
   /// </summary>
   /// <typeparam name="T">Type of elements in resulting collection.</typeparam>
   /// <typeparam name="TEval">Type of data that will be evaluated.</typeparam>
   internal class LazyEnumerator<T, TEval> : IEnumerator<T>
   {
      private LazyEnumerable<T, TEval> lazyEnumerable;
      private bool isOnBeginning;
      private TEval nextEvaluatorValue;

      /// <summary>
      /// Initializes a new instance of the <see cref="LazyEnumerator&lt;T, TEval&gt;"/> class.
      /// </summary>
      /// <param name="lazyEnumerable">The lazy collection that will be enumerated over.</param>
      public LazyEnumerator(LazyEnumerable<T, TEval> lazyEnumerable)
      {
         this.lazyEnumerable = lazyEnumerable;
         Reset();
      }

      /// <summary>
      /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
      /// </summary>
      public void Dispose()
      {
         lazyEnumerable = null;
      }

      /// <summary>
      /// Advances the enumerator to the next element of the collection.
      /// </summary>
      /// <returns>
      /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
      /// </returns>
      public bool MoveNext()
      {
         bool result = false; 

         TEval valueForEvaluator;

         if (isOnBeginning)
         {
            valueForEvaluator = lazyEnumerable.InitialEvaluatorValue;
            isOnBeginning = false;
         }
         else
         {
            valueForEvaluator = nextEvaluatorValue;
         }

         Option<T, TEval> option = lazyEnumerable.Evaluator(valueForEvaluator);
         if (option.AddCurrent)
         {
            result = true;
            Current = option.Current;
            nextEvaluatorValue = option.NextEvaluatorValue;
         }

         return result;
      }

      /// <summary>
      /// Sets the enumerator to its initial position, which is before the first element in the collection.
      /// </summary>
      /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
      public void Reset()
      {
         isOnBeginning = true;

         Current = default(T);
         nextEvaluatorValue = default(TEval);
      }

      /// <summary>
      /// Gets the current element of collection.
      /// </summary>
      public T Current { get; private set; }

      /// <summary>
      /// Gets the current element of collection.
      /// </summary>
      object IEnumerator.Current
      {
         get { return Current; }
      }
   }
}